#region Copyright (c) 2003-2007, Luke T. Maxon



/********************************************************************************************************************

'

' Copyright (c) 2003-2007, Luke T. Maxon

' All rights reserved.

' 

' Redistribution and use in source and binary forms, with or without modification, are permitted provided

' that the following conditions are met:

' 

' * Redistributions of source code must retain the above copyright notice, this list of conditions and the

' 	following disclaimer.

' 

' * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and

' 	the following disclaimer in the documentation and/or other materials provided with the distribution.

' 

' * Neither the name of the author nor the names of its contributors may be used to endorse or 

' 	promote products derived from this software without specific prior written permission.

' 

' THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED

' WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A

' PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR

' ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

' LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS

' INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,

' OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN

' IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

'

'*******************************************************************************************************************/



#endregion



using System;

using System.Reflection;



namespace SuedeTest.Core {



   public class InvokeTimeoutException : Exception {

      public InvokeTimeoutException() {

      }

      public InvokeTimeoutException(string s)

         : base(s) {



      }

   }



   ///<summary>

   /// Methods for firing events via reflection.

   ///</summary>

   public static class EventHelper {

      public static void RaiseCustomEvent(object targetObject, string eventName) {

         MethodInfo minfo = null;
         try {
            minfo = targetObject.GetType().GetMethod(eventName,

                                                                BindingFlags.Instance | BindingFlags.Public |

                                                                BindingFlags.NonPublic);
         } catch (AmbiguousMatchException) {
            MethodInfo[] methods = targetObject.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

            for (int x = 0; x < methods.Length; x++) {
               if (methods[x].Name == eventName) {
                  minfo = methods[x];
                  x = methods.Length;
               }
            }
         }
         ParameterInfo[] param = minfo.GetParameters();



         object[] defaultArgs = new object[param.Length];

         for (int x = 0; x < param.Length; x++) {

            defaultArgs[x] = Activator.CreateInstance(param[x].ParameterType);

         }



         InvokeMethod(targetObject, defaultArgs, minfo);

      }





      ///<summary>

      /// Fires the named event on the given object using the object's "OnEventName" method.

      ///</summary>

      /// <remarks>

      /// <para>

      /// By convention, an event named "MyEvent(object sender, MyEventArgs e)" should have a virtual protected

      /// method "OnMyEvent(MyEventArgs e)" that actually calls any attached event handler.

      /// </para>

      /// <para>

      /// This method assumes that the target event has been implemented with this pattern.

      /// </para>

      /// </remarks>

      ///<param name="targetObject">The object raising the event.</param>

      ///<param name="eventName">The name of the event to raise.</param>

      public static void RaiseEvent(object targetObject, string eventName) {

         RaiseCustomEvent(targetObject, "On" + eventName);

      }



      ///<summary>

      /// Fires the named event on the given object using the object's "OnEventName" method.

      ///</summary>

      /// <remarks>

      /// <para>

      /// By convention, an event named "MyEvent(object sender, MyEventArgs e)" should have a virtual protected

      /// method "OnMyEvent(MyEventArgs e)" that actually calls any attached event handler.

      /// </para>

      /// <para>

      /// This method assumes that the target event has been implemented with this pattern.

      /// </para>

      /// </remarks>

      ///<param name="targetObject">The object raising the event.</param>

      ///<param name="eventName">The name of the event to raise.</param>

      ///<param name="args">A list of arguments to pass to the EventArgs-derived parameter's constructor.</param>

      public static void RaiseEvent(object targetObject, string eventName, object[] args) {

         MethodInfo minfo = targetObject.GetType().GetMethod("On" + eventName,

                                                             BindingFlags.Instance | BindingFlags.Public |

                                                             BindingFlags.NonPublic);

         ParameterInfo[] param = minfo.GetParameters();

         Type parameterType = param[0].ParameterType;



         InvokeMethod(targetObject, new object[] { Activator.CreateInstance(parameterType, args) }, minfo);

      }



      private static System.Threading.ManualResetEvent _waitHandle = new System.Threading.ManualResetEvent(false);

      private static System.Timers.Timer _invokeTimeout = null;

      private static bool _invokeError = false;



      private static object InvokeMethod(object targetObject, object[] args, MethodInfo minfo) {

         object result = null;

         System.Windows.Forms.Control control = null;



         Type t = targetObject.GetType();



         if (t.IsSubclassOf(typeof(System.Windows.Forms.Control))) {

            control = targetObject as System.Windows.Forms.Control;

         } else if (t.IsSubclassOf(typeof(System.Windows.Forms.ToolStripItem))) {

            control = (targetObject as System.Windows.Forms.ToolStripItem).Owner;

         }



         if (control == null) {

            throw new Exception ("Couldn't find a control to invoke on for " + targetObject.ToString ());

         }



         if (!control.IsHandleCreated || control.IsDisposed) {

            throw new NoSuchControlException("Control isn't ready yet!");

         }



         control.BeginInvoke((System.Windows.Forms.MethodInvoker)delegate() {

            result = minfo.Invoke(targetObject, args);

            _waitHandle.Set();

         });



         InitTimer();



         if (_invokeTimeout != null) {

            _invokeTimeout.Start();

         }



         _waitHandle.WaitOne();



         if (_invokeTimeout != null) {

            _invokeTimeout.Stop();

         }

         

         _waitHandle.Reset();



         if (_invokeError) {

            throw new InvokeTimeoutException();

         }



         return result;

      }



      private static void InitTimer() {

         if (_invokeTimeout == null) {

            _invokeError = false;



            _invokeTimeout = new System.Timers.Timer();

            _invokeTimeout.Interval = 60000;

            _invokeTimeout.AutoReset = false;

            _invokeTimeout.Elapsed += new System.Timers.ElapsedEventHandler(_invokeTimeout_Elapsed);

         }

      }



      private static void _invokeTimeout_Elapsed(object sender, System.Timers.ElapsedEventArgs e) {

         _invokeError = true;

         _waitHandle.Set();

      }



      /// <summary>

      /// Causes the test-runner to begin executing other test-instructions without waiting

      /// for the current one to finish.  Should ONLY be used with blocking/modal dialogs.

      /// </summary>

      public static void ReleaseWaitHandle() {

         _waitHandle.Set();

      }

   }

}